home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.04 Apr 94 / Powering Up / Executing Code / SimpleApp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-09  |  9.3 KB  |  372 lines  |  [TEXT/MPCC]

  1. // File: SimpleApp.c
  2. //
  3. // This is a trivial PowerPC application which loads and runs external code modules
  4. // from PEF containers in either resources or the Data fork of a file.
  5. //
  6. // A more complete example can be found in "ModApp", which is located in the Macintosh
  7. // on RISC Software Developer's Kit, in the Metrowerks "Code Warrior" product, or in
  8. // the classroom courses at Apple's "Developer University."
  9. //
  10.  
  11. #ifndef powerc
  12.     #error This code only runs on a PowerPC machine.
  13. #endif
  14.  
  15. #include <Types.h>
  16. #include <Memory.h>
  17. #include <Resources.h>
  18. #include <Quickdraw.h>
  19. #include <Menus.h>
  20. #include <Events.h>
  21. #include <Windows.h>
  22. #include <Dialogs.h>
  23. #include <AppleEvents.h>
  24. #include <StandardFile.h>
  25. #include <Files.h>
  26.  
  27. #include <FragLoad.h>
  28.  
  29.  
  30. // === The interface to our example code
  31. typedef Boolean (*MainRoutinePtr) (EventRecord *theEvent, WindowPtr currWindow,
  32.                                 QDGlobals *qdAddress);
  33.  
  34. // === Useful constants
  35. enum {
  36.         kAppleMenu = 128,
  37.         kFileMenu  = 129,
  38.             kOpenCommand = 1,
  39.             kQuitCommand = 3
  40.     };
  41.  
  42. enum {
  43.         rAboutAlert = 128,
  44.         rErrorAlert = 129,
  45.         rMainWindow = 128
  46.     };
  47.  
  48. enum {
  49.         kPEFData = 'DPEF',
  50.         kPEFResource = 'RPEF'
  51.     };
  52.  
  53. // === Global variables
  54. QDGlobals        qd;
  55. Boolean            gDone = false;
  56. MainRoutinePtr    gMain = NULL;            // If we have some code loaded, this is the entry point
  57. ConnectionID    gConnID = 0;            // A "reference number" for our loaded PowerPC code
  58. Handle            gCodeResource = NULL;    // If we loaded the code as a resource, this is a 
  59.                                         // handle to the resource
  60. WindowPtr        gOurWindow = NULL;
  61.  
  62. // === Function prototypes
  63. void Initialize (void);
  64. void MainLoop (void);
  65. void DoMouseDown (EventRecord theEvent);
  66. void DoMenu (long menuCode);
  67. void FindAndOpenCode (void);
  68. void LoadCode (FSSpec fileSpec, OSType fileType);
  69. void UnloadCode(void);
  70. void LoadFailed(OSErr err, Str255 errName);
  71. pascal OSErr HandleOAPP (AEDescList *aevt, AEDescList *reply, long refCon);
  72. pascal OSErr HandleODOC (AEDescList *aevt, AEDescList *reply, long refCon);
  73. pascal OSErr HandleQUIT (AEDescList *aevt, AEDescList *reply, long refCon);
  74.  
  75. // === Start of our code
  76. main()
  77. {
  78.     Initialize();
  79.     MainLoop();
  80. }
  81.  
  82.  
  83. // Initialize the ROM managers, set up our menu bar and (only) window, and
  84. // install our Apple event handlers.
  85. void Initialize()
  86. {
  87.     MenuHandle    appleMenu;
  88.     MenuHandle    fileMenu;
  89.     
  90.     // Standard "boilerplate" initialization
  91.     InitGraf(&qd.thePort);
  92.     InitFonts();
  93.     InitWindows();
  94.     InitMenus();
  95.     TEInit();
  96.     InitDialogs(NULL);
  97.     FlushEvents(everyEvent, 0);
  98.  
  99.     // Set up the menu bar
  100.     appleMenu = GetMenu(kAppleMenu);
  101.     if (appleMenu == NULL) {
  102.         // Our resource file is missing! Bail out!
  103.         ExitToShell();
  104.     }
  105.     AddResMenu(appleMenu, 'DRVR');    // Add Desk Accessories
  106.     InsertMenu(appleMenu, 0);
  107.     fileMenu = GetMenu(kFileMenu);
  108.     InsertMenu(fileMenu, 0);
  109.     DrawMenuBar();
  110.     
  111.     // Construct a window
  112.     gOurWindow = GetNewCWindow(rMainWindow, NULL, (WindowPtr)-1L);
  113.     SetPort(gOurWindow);
  114.  
  115.     // Install our Apple event handlers
  116.     AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc(HandleOAPP), 0, false);
  117.     AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,   NewAEEventHandlerProc(HandleODOC), 0, false);
  118.     AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,  NewAEEventHandlerProc(HandleQUIT), 0, false);
  119.  
  120.     InitCursor();
  121. }
  122.  
  123.  
  124. // Collect events until the user selects "Quit" (or we get a "Quit" Apple
  125. // event.) If we've loaded some PowerPC code, then give that code a chance
  126. // to execute the event first.
  127. void MainLoop()
  128. {
  129.     EventRecord    theEvent;
  130.     
  131.     while (!gDone) {
  132.         WaitNextEvent(everyEvent, &theEvent, 15L, NULL);        
  133.         // Do we have some code loaded? If so, pass the event to that code
  134.         if (gMain != NULL) {
  135.             Boolean    eventHandled;
  136.             
  137.             eventHandled = gMain(&theEvent, FrontWindow(), &qd);
  138.             if (eventHandled)
  139.                 continue;    // The external code handled the event, so get another
  140.         }
  141.  
  142.         // The external code didn't handle the event, so we will
  143.         switch (theEvent.what) {
  144.             case mouseDown: 
  145.                 DoMouseDown(theEvent);
  146.             break;
  147.  
  148.             case keyDown: {
  149.                 // See if the comamnd key is down. If it is, handle this as
  150.                 // a menu command
  151.                 char    theKey = (theEvent.message & charCodeMask);
  152.  
  153.                 if (theEvent.modifiers & cmdKey)
  154.                     DoMenu(MenuKey(theKey));
  155.             }
  156.             break;
  157.  
  158.             case updateEvt: {
  159.                 WindowPtr    theWindow = (WindowPtr)theEvent.message;
  160.                 
  161.                 BeginUpdate(theWindow);
  162. //                EraseRect(&theWindow->portRect);
  163.                 EndUpdate(theWindow);
  164.             }
  165.             break;
  166.                         
  167.             case kHighLevelEvent:
  168.                 AEProcessAppleEvent(&theEvent);
  169.         }
  170.     }
  171. }
  172.  
  173. // Handle mouse clicks either in the menubar or in a window
  174. void DoMouseDown (EventRecord theEvent)
  175. {
  176.     short        part;
  177.     WindowPtr    theWindow;
  178.  
  179.     part = FindWindow(theEvent.where, &theWindow);
  180.     switch (part) {
  181.         case inMenuBar: 
  182.             DoMenu(MenuSelect(theEvent.where));
  183.         break;
  184.  
  185.         case inSysWindow: 
  186.             SystemClick(&theEvent, theWindow);
  187.         break;
  188.  
  189.         case inDrag: {
  190.             Rect dragRect = qd.screenBits.bounds;
  191.             dragRect.top = GetMBarHeight();
  192.             DragWindow(theWindow, theEvent.where, &dragRect);
  193.         }
  194.         break;
  195.     }
  196. }
  197.  
  198.  
  199. // Code to handle the File and Apple menus
  200. void DoMenu (long menuCode)
  201. {
  202.     short    menuID = menuCode >> 16;
  203.     short    itemID = menuCode & 0x0000FFFF;
  204.     
  205.     switch (menuID) {
  206.         case kAppleMenu:
  207.             if (itemID == 1)
  208.                 Alert(128, NULL); // Display the "About" box
  209.         break;
  210.         
  211.         case kFileMenu:
  212.             if (itemID == kOpenCommand) {
  213.                 FindAndOpenCode();
  214.             }
  215.             else if (itemID == kQuitCommand)
  216.                 gDone = true;
  217.         break;
  218.     }
  219. }
  220.  
  221.  
  222. // Ask the user to locate a bit of code for us, then load
  223. // and execute the code
  224. void FindAndOpenCode ()
  225. {
  226.     // Open a file of type 'PEF ' or a resource file
  227.     SFTypeList typeList = {kPEFData, kPEFResource};
  228.     StandardFileReply     reply;
  229.  
  230.     StandardGetFile(NULL, 3, typeList, &reply);
  231.     if (reply.sfGood)
  232.         LoadCode(reply.sfFile, reply.sfType);
  233. }
  234.  
  235.  
  236. // Load a bit of PowerPC code which is external to this application
  237. void LoadCode (FSSpec fileSpec, OSType fileType)
  238. {
  239.     OSErr    err = noErr;
  240.     Str255    errName;
  241.  
  242.     // We have a file, so open it and load the code
  243.     // But first, we have to unload any previous code...
  244.     UnloadCode();
  245.     
  246.     if (fileType == kPEFResource) {
  247.         // It's a resource file, so load the code from a resource
  248.         short    refNum;
  249.         
  250.         refNum = FSpOpenResFile(&fileSpec, fsCurPerm);
  251.         if (ResError() == noErr) {
  252.             gCodeResource = Get1IndResource(kPEFResource, 1);
  253.             if (gCodeResource != NULL) {
  254.                 DetachResource(gCodeResource);
  255.                 HLock(gCodeResource);
  256.                 // We have the code, but it's not ready to use yet
  257.                 // We have to ask the Code Fragment Manager to "prepare"
  258.                 // the code for execution and return the "main" address
  259.                 // to us
  260.                 err = GetMemFragment (*gCodeResource, 0, fileSpec.name,
  261.                                         kLoadNewCopy, &gConnID, (Ptr*)&gMain,
  262.                                         errName);
  263.                 if (err) {
  264.                     // Something went wrong, so tell the user
  265.                     LoadFailed(err, errName);
  266.                     UnloadCode();
  267.                 }
  268.             }
  269.             CloseResFile(refNum);
  270.         }
  271.         
  272.     } else {
  273.         // It's not a resource file, so load the code from the data fork
  274.         
  275.         err = GetDiskFragment (&fileSpec, 0, 0, fileSpec.name,
  276.                                 kLoadNewCopy, &gConnID, (Ptr*)&gMain,
  277.                                 errName);
  278.         if (err) {
  279.             // Something went wrong, so tell the user
  280.             LoadFailed(err, errName);
  281.             UnloadCode();
  282.         }
  283.     }
  284.     if (err == noErr) {
  285.         // Invalidate the window so the external code gets a chance to
  286.         // draw in it
  287.         SetPort(gOurWindow);
  288.         InvalRect(&gOurWindow->portRect);
  289.     }
  290. }
  291.  
  292.  
  293. // If some code is already loaded, this routine tells the CFM to unload the code
  294. // and then disposes of the memory (if the code was loaded in from a resource)_
  295. void UnloadCode()
  296. {
  297.     if (gMain != NULL) {
  298.         // Unload the old module
  299.         CloseConnection(&gConnID);
  300.         gMain = NULL;
  301.     }
  302.         
  303.     // If the old code was in a resource, release the memory
  304.     // (we've already detached the resource, so we can free it
  305.     // with a simple DisposeHandle)
  306.     if (gCodeResource != NULL) {
  307.         DisposeHandle(gCodeResource);
  308.         gCodeResource = NULL;
  309.     }
  310. }
  311.  
  312.  
  313. // Report an error in loading some code
  314. void LoadFailed(OSErr err, Str255 errName)
  315. {
  316.     Str31    errNumString;
  317.     
  318.     NumToString(err, errNumString);
  319.     ParamText(errNumString, errName, NULL, NULL);
  320.     Alert(rErrorAlert, NULL);
  321. }
  322.  
  323.  
  324. // Apple event handlers for "Open Application", "Open Document", and
  325. // "Quit"
  326. pascal OSErr HandleOAPP (AEDescList *aevt, AEDescList *reply, long refCon)
  327. {
  328.     FindAndOpenCode();
  329.     return noErr;
  330. }
  331.  
  332.  
  333. pascal OSErr HandleODOC (AEDescList *aevt, AEDescList *reply, long refCon)
  334. // We'll get this if somebody double-clicks on one of our code files
  335. {
  336.     AEDesc        fileListDesc = {'NULL', NULL};
  337.     FSSpec        theFile;
  338.     OSErr        err;
  339.                             
  340.     // Extract an alias to the file into "fileListDesc"
  341.     // (if the user selected multiple files, we'll only look at the
  342.     // first one, but asking for a list just simplifies the code.)
  343.     err = AEGetKeyDesc( aevt, keyDirectObject, typeAEList, &fileListDesc );
  344.     if (err == noErr) {
  345.         // Get the first file in the list
  346.         // Even though the event contains a list of alises, the Apple Event Manager
  347.         // will convert each alias to an FSSpec if we ask it to.
  348.         DescType    actualType;
  349.         long        actualSize;
  350.         AEKeyword    actualKeyword;
  351.  
  352.         err = AEGetNthPtr( &fileListDesc, 1, typeFSS, &actualKeyword,
  353.                             &actualType, (Ptr)&theFile, sizeof(theFile), &actualSize);
  354.         if (err == noErr) {
  355.             // LoadCode wants the file's type, so we have to get that
  356.             FInfo    finderInfo;
  357.             
  358.             FSpGetFInfo(&theFile, &finderInfo);
  359.             LoadCode(theFile, finderInfo.fdType);
  360.         }
  361.     }
  362.     AEDisposeDesc(&fileListDesc);
  363.     return err;
  364. }
  365.  
  366.  
  367. pascal OSErr HandleQUIT (AEDescList *aevt, AEDescList *reply, long refCon)
  368. {
  369.     gDone = true;
  370.     return noErr;
  371. }
  372.